package dog.weatherwallpaper;

import java.io.File;
import java.util.Arrays;
import java.util.List;

import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.Paint;
import android.graphics.Typeface;
import android.os.Build;
import android.os.Handler;
import android.service.wallpaper.WallpaperService;
import android.support.v4.content.LocalBroadcastManager;
import android.util.DisplayMetrics;
import android.view.GestureDetector;
import android.view.MotionEvent;
import android.view.SurfaceHolder;
import android.widget.Toast;
import dog.weatherwallpaper.model.CurrentWeather;
import dog.weatherwallpaper.model.FutureWeather;
import dog.weatherwallpaper.model.Unit;
import dog.weatherwallpaper.model.Weather;
import dog.weatherwallpaper.model.WeatherInfo;
import dog.weatherwallpaper.utils.App;
import dog.weatherwallpaper.utils.ComplementaryColor;
import dog.weatherwallpaper.utils.Conversion;
import dog.weatherwallpaper.utils.Internet;
import dog.weatherwallpaper.utils.Time;

public final class WPService extends WallpaperService {
	public static final String DRAW = "draw";
	private final Handler handler = new Handler();

	@Override
	public Engine onCreateEngine() {
		return new WeatherWallpaperEngine();
	}

	@Override
	public int onStartCommand(Intent intent, int flags, int startId) {
		if (intent == null) {
			startService(new Intent(getBaseContext(), WPService.class));
		}
		return START_STICKY;
	}

	final class WeatherWallpaperEngine extends Engine {
		private final Paint paint = new Paint();

		WeatherWallpaperEngine() {
			paint.setAntiAlias(true);
			handler.post(drawRunnable);
			LocalBroadcastManager.getInstance(getApplicationContext()).registerReceiver(receiver,
					new IntentFilter(DRAW));
		}

		private BroadcastReceiver receiver = new BroadcastReceiver() {
			@Override
			public void onReceive(Context context, Intent intent) {
				handler.post(drawRunnable);
			}
		};

		private final Runnable drawRunnable = new Runnable() {
			@Override
			public void run() {
				SurfaceHolder holder = getSurfaceHolder();
				Canvas canvas = null;
				try {
					canvas = holder.lockCanvas();
					if (canvas != null) {
						draw(canvas);
					}
				} finally {
					if (canvas != null) {
						holder.unlockCanvasAndPost(canvas);
					}
				}

				handler.removeCallbacks(drawRunnable);
				if (isVisible()) {
					int updateInterval = App.getIntPreference(R.string.update_interval_key,
							R.string.update_interval_default);
					handler.postDelayed(drawRunnable, updateInterval * Time.MILLIS_IN_AN_HOUR);
				}
			}
		};

		private void draw(Canvas canvas) {
			boolean image = App.getBooleanPreference(R.string.image_key);
			String bgImage = App.getStringPreference(R.string.bg_image_key);
			String bgColor = App.getStringPreference(R.string.bg_color_key, R.string.bg_color_default);

			if (image && new File(bgImage).exists()) {
				Bitmap bitmap = BitmapFactory.decodeFile(bgImage);
				int width = 0, height = 0;
				final DisplayMetrics dm = getResources().getDisplayMetrics();
				if (dm.heightPixels > dm.widthPixels) {
					height = dm.heightPixels;
					double ratio = (double) bitmap.getHeight() / height;
					width = (int) Math.round(bitmap.getWidth() / ratio);
				} else {
					width = dm.widthPixels;
					double ratio = (double) bitmap.getWidth() / width;
					height = (int) Math.round(bitmap.getHeight() / ratio);
				}
				bitmap = Bitmap.createScaledBitmap(bitmap, width, height, true);
				canvas.drawBitmap(bitmap, 0, 0, paint);
			} else {
				canvas.drawColor(Color.parseColor(bgColor));
			}

			boolean bold = App.getBooleanPreference(R.string.bold_key);
			boolean italic = App.getBooleanPreference(R.string.italic_key);
			String typeface = App.getStringPreference(R.string.typeface_key, R.string.typeface_default);

			int style = (bold ? 1 : 0) + (italic ? 2 : 0);
			if (Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH && typeface.equals("Roboto")) {
				List<String> filenames = Arrays.asList("Roboto-Regular.ttf", "Roboto-Bold.ttf", "Roboto-Italic.ttf",
						"Roboto-BoldItalic.ttf");
				paint.setTypeface(Typeface.createFromAsset(getAssets(), filenames.get(style)));
			} else {
				paint.setTypeface(Typeface.create(typeface, style));
			}

			String textColor = App.getStringPreference(R.string.text_color_key, R.string.text_color_default);
			int textSize = App.getIntPreference(R.string.text_size_key, R.string.text_size_default);
			int spacing = App.getIntPreference(R.string.line_spacing_key, R.string.line_spacing_default);

			paint.setTextSize(Conversion.dpToPx(textSize));
			int textColorCode = Color.parseColor(textColor);
			int borderColorCode = new ComplementaryColor(textColorCode).getCode();
			paint.setColor(textColorCode);
			paint.setShadowLayer(3, 0, 0, borderColorCode);

			int days = App.getIntPreference(R.string.days_key, R.string.days_default);
			boolean extraCurrentInfo = App.getBooleanPreference(R.string.extra_info_key);
			int screenHeight = Conversion.pxToDp(getBaseContext().getResources().getDisplayMetrics().heightPixels);
			int fullTextSize = (days + (extraCurrentInfo ? 1 : 0)) * (textSize + spacing);
			int position = App.getIntPreference(R.string.position_key, R.string.position_default);
			if (position > screenHeight - fullTextSize) {
				position = screenHeight - fullTextSize;
			}
			position = Conversion.dpToPx(position);

			boolean celsius = App.getBooleanPreference(R.string.temperature_key);
			boolean km = App.getBooleanPreference(R.string.distance_key);
			Unit unit = new Unit(celsius, km);

			WeatherInfo wi = WeatherInfo.getInstance();
			if (new Internet().isConnected() && wi.isOldWeather() && !wi.update()) {
				App.toast(R.string.update_failed, Toast.LENGTH_LONG);
			}
			Weather weather = wi.getWeather();

			CurrentWeather now = weather.getCurrent();
			String text = now.getBasicInfo(unit);
			canvas.drawText(text, 0, position, paint);
			int lineHeight = Conversion.dpToPx(textSize + spacing);
			if (extraCurrentInfo) {
				text = now.getExtraInfo(km);
				canvas.drawText(text, 0, position += lineHeight, paint);
			}

			for (int i = 0; i < days; i++) {
				FutureWeather day = weather.getDay(i);
				text = day.getBasicInfo(unit);
				canvas.drawText(text, 0, position += lineHeight, paint);
			}
		}

		// for unknown reasons, double tap does not work in 2.2- (affects 1.5% of 1 000 000 000 devices)
		private GestureDetector gestureDetector;

		@Override
		public void onCreate(SurfaceHolder surfaceHolder) {
			super.onCreate(surfaceHolder);
			gestureDetector = new GestureDetector(getApplicationContext(),
					new GestureDetector.SimpleOnGestureListener() {
						@Override
						public boolean onDoubleTap(MotionEvent e) {
							App.startActivity(MainActivity.class);
							return true;
						}
					});
		}

		@Override
		public void onTouchEvent(MotionEvent event) {
			gestureDetector.onTouchEvent(event);
		}

		@Override
		public void onVisibilityChanged(boolean visible) {
			if (visible) {
				handler.post(drawRunnable);
			} else {
				handler.removeCallbacks(drawRunnable);
			}
		}

		@Override
		public void onSurfaceChanged(SurfaceHolder holder, int format, int width, int height) {
			super.onSurfaceChanged(holder, format, width, height);
			handler.post(drawRunnable);
		}

		@Override
		public void onSurfaceDestroyed(SurfaceHolder holder) {
			super.onSurfaceDestroyed(holder);
			handler.removeCallbacks(drawRunnable);
		}

		@Override
		public void onDestroy() {
			super.onDestroy();
			handler.removeCallbacks(drawRunnable);
			LocalBroadcastManager.getInstance(getApplicationContext()).unregisterReceiver(receiver);
		}
	}
}